package com.vne.skyemart.util;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.PrintStream;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.vne.skyemart.exception.KeyNotFoundException;



public class Config {
	private Properties primary;
	private ByteArrayOutputStream dbgBytes;
	private PrintStream dbgStream;
	private boolean warnGet = false;

	public Config() {
		this(new Properties());
	}

	public Config(Properties p) {
		primary = p;
		dbgBytes = new ByteArrayOutputStream();
		dbgStream = new PrintStream(dbgBytes);

		dbgStream.println("Config.java debugInfo:");
		dbgStream
				.println("initialized with following properties file contents(");
		primary.list(dbgStream);
		dbgStream.println(")");
	}

	public boolean hasProperty(String key) {
		if (null == key) {
			return false;
		}

		String s = primary.getProperty(key);
		return s != null;
	}

	public Properties getAllProperties() {
		Properties giveback = new Properties();
		giveback.putAll(primary);
		return giveback;
	}

	public synchronized String getProperty(String key) throws KeyNotFoundException {
		if (null == key) {
			throwNoKey("<null>");
		}

		String s = primary.getProperty(key);
		if (s == null) {
			throwNoKey(key);
		}

		if (warnGet) {
			System.err.println("warning, get called with no default for key: "
					+ key);
		}

		return s.trim();
	}

	public synchronized String getProperty(String key, String defaultValue) {
		if (key == null) {
			return defaultValue;
		}
		String s = primary.getProperty(key);
		if (s == null) {
			return defaultValue;
		}
		return s.trim();
	}

	/**
	 * @param key
	 *            key to set.
	 * @param value
	 *            value to set the key to.
	 * @return string
	 */
	public synchronized String setProperty(String key, String value) {
		if (key != null && value != null) {
			primary.setProperty(key, value);
			return primary.getProperty(key);
		}
		return null;
	}

	public synchronized void loadConfig(Config rhs) {
		dbgStream.println("adding config, here is *its* debug info: ");
		dbgStream.println(rhs.getDebugInfo() + "\n and now adding its props:");
		loadProperties(rhs.getAllProperties());
		dbgStream.println("done loading other config");
	}

	
	@SuppressWarnings("rawtypes")
	public synchronized void loadProperties(Properties props) {
		dbgStream.println(" adding properties (");
		if (props == null) {
			dbgStream.println("null!\n)");
			return;
		}
		props.list(dbgStream);
		dbgStream.println(")");
		for (Enumeration e = props.propertyNames(); e.hasMoreElements();) {
			String key = (String) e.nextElement();
			setProperty(key, props.getProperty(key));
		}
	}

	@SuppressWarnings("rawtypes")
	public synchronized void loadEnv() {
		dbgStream.println("loading system properties");
		loadProperties(System.getProperties());
		dbgStream.println("loading environment properties");

		for (Map.Entry me : System.getenv().entrySet()) {
			setProperty(me.getKey().toString().replace("__2E__", "."), me
					.getValue().toString());
		}
	}

	public synchronized void loadFile(String filename) throws IOException {
		loadFile(filename, true);
	}

	public synchronized void loadFile(String filename, boolean mustExist)
			throws IOException {
		dbgStream.println("loading file " + filename);
		LineNumberReader lnr;

		// Check if have a space in file name
		filename = (filename.contains("%20")) ? filename.replace("%20", " ")
				: filename;

		try {
			lnr = new LineNumberReader(new FileReader(filename));
		} catch (FileNotFoundException ioe) {
			if (mustExist) {
				throw ioe;
			}
			// not using LOG here because it depends on us to build, and
			// I dont want to create a circular jar file dependency that
			// will make builds without bootstrap binary jar files impossible.
			// LOG.warn("warning, missing property file:", ioe);
			System.err.println("warning, missing property file: "
					+ ioe.getMessage());
			return;
		}

		Pattern kvpair = Pattern.compile("^([a-zA-Z0-9._-]+?)=(.*)$");
		for (String line = lnr.readLine(); line != null; line = lnr.readLine()) {
			Matcher m = kvpair.matcher(line);

			if (m.matches() && m.groupCount() == 2) {
				String k = m.group(1);
				String v = m.group(2);
				String dbg = "adding line[" + line + "] so k[" + k + "] v[" + v
						+ "]";
				dbgStream.println(dbg);
				// System.err.println(dbg);
				setProperty(k, v);
			}
		}

		lnr.close();
	}

	public synchronized void loadArgs(String[] mainargs, boolean override) {
		if (mainargs == null) {
			return;
		}

		Pattern pat = Pattern.compile("=");
		String[] kv;
		for (String mainarg : mainargs) {
			kv = pat.split(mainarg, 2);
			if (kv.length == 2) {
				String k = kv[0];
				String v = kv[1];

				if (k.matches("-D.*")) {
					k = k.replaceFirst("^-D", "");
				}
				dbgStream.println("load arg key=" + k + " value=" + v);
				setProperty(k, v);
			}
		}
	}

	public synchronized String getDebugInfo() {
		dbgStream.println("\n\nall current properties(");
		primary.list(dbgStream);
		dbgStream.println(")");
		return dbgBytes.toString();
	}

	public synchronized void setWarnGet(boolean newWarnGet) {
		this.warnGet = newWarnGet;
	}

	private void throwNoKey(String missingKey) throws KeyNotFoundException {
		throw new KeyNotFoundException(missingKey, getDebugInfo());
	}
}
